
package com.hlkj.pay.app.merchant.ext.impl.haike;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.framework.extension.annotation.Extension;
import com.hlkj.framework.extension.executor.ExtensionExecutor;
import com.hlkj.framework.extension.scenario.BizScenario;
import com.hlkj.pay.app.merchant.ext.ChannelMerchantOrderExtPt;
import com.hlkj.pay.app.merchant.ext.MerchantExtConstant;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelPayOrderContext;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelPayOrderQueryContext;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelRefundPayOrderContext;
import com.hlkj.pay.app.merchant.ext.impl.haike.dto.HaikePayConfig;
import com.hlkj.pay.app.merchant.ext.impl.haike.dto.resp.HaikeOrderRefundNotifyResp;
import com.hlkj.pay.app.merchant.ext.impl.haike.dto.resp.HaikePrePayNotifyResp;
import com.hlkj.pay.app.merchant.ext.impl.haike.handler.HaikePayCommonParamsHandle;
import com.hlkj.pay.app.merchant.ext.impl.leshua.constant.LeshuaPayExtConstant;
import com.hlkj.pay.app.merchant.ext.impl.yeepay.constant.YeePayRsaUtil;
import com.hlkj.pay.app.merchant.ext.impl.yeepay.dto.YeePayConfig;
import com.hlkj.pay.app.merchant.ext.impl.yeepay.dto.YeePayCsNotify;
import com.hlkj.pay.common.MerchantResultCode;
import com.hlkj.pay.dto.order.PayOrderChannelResultDto;
import com.hlkj.pay.dto.order.notify.CsOrderNotifyDto;
import com.hlkj.pay.dto.order.notify.RefundOrderNotifyDto;
import com.hlkj.pay.enums.CommonEnum;
import com.hlkj.pay.enums.PayEnums;
import com.hlkj.pay.enums.PayOrderEnums;
import com.hlkj.pay.infrastructure.model.order.PayOrderDO;
import com.hlkj.pay.infrastructure.model.order.RefundOrderDO;
import com.hlkj.pay.infrastructure.model.pay.PayWayDO;
import com.hlkj.pay.infrastructure.model.pay.ProviderChannelConfigDO;
import com.hlkj.pay.util.DateUtils;
import com.hlkj.pay.vo.order.req.PayOrderCommonParamReq;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * 海科订单操作
 *
 * @author HlpayTeam
 * @date 2024/10/10 13:51
 */
@Slf4j
@Repository
@RequiredArgsConstructor
@Extension(bizId = MerchantExtConstant.CHANNEL_HAIKE_PAY)
public class HaikePayMerchantOrderExtPt implements ChannelMerchantOrderExtPt {

    private final ExtensionExecutor extensionExecutor;

    private final HaikePayCommonParamsHandle haikePayCommonParamsHandle;

    @Override
    public CommonResult<String> channelOrderCode(ChannelPayOrderContext channelPayOrderContext) {
        return CommonResult.success();
    }

    @Override
    public void submitChannelPayOrder(ChannelPayOrderContext channelPayOrderContext) {
        PayWayDO payWayDO = channelPayOrderContext.getPayWayDO();
        // 额外参数
        PayOrderCommonParamReq.Extra extra = channelPayOrderContext.getExtra();
        PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.from(payWayDO.getSubType());
        String yeePay = null;
        // 非原生
        if(extra.getOriginalType() == CommonEnum.YES_NO_TYPE.NO.getCode()){
            // 微信 H5, APP 非原生 走小程序
            if(payWayDO.getType() == PayEnums.PAY_TYPE.WECHAT.getCode()){
                switch (scenePayType) {
                    case H5:
                    case APP:
                    case MINI_APP:
                        yeePay = LeshuaPayExtConstant.JSAPI_PAY;;
                        break;
                    default:
                        yeePay = pay(payWayDO);
                        break;
                }
            // 支付宝 H5,APP 非原生 走扫码
            }else if(payWayDO.getType() == PayEnums.PAY_TYPE.ALIPAY.getCode()){
                switch (scenePayType) {
                    case H5:
                    case APP:
                        yeePay = LeshuaPayExtConstant.NATIVE_PAY;;
                        break;
                    default:
                        yeePay = pay(payWayDO);
                        break;
                }
            }else{
                yeePay = pay(payWayDO);
            }

        }else{
            // 支付宝 H5,APP 走扫码
            if(payWayDO.getType() == PayEnums.PAY_TYPE.ALIPAY.getCode()){
                switch (scenePayType) {
                    case H5:
                    case APP:
                        yeePay = LeshuaPayExtConstant.NATIVE_PAY;;
                        break;
                    default:
                        yeePay = pay(payWayDO);
                        break;
                }
            }else{
                yeePay = pay(payWayDO);
            }
        }

        if (!StringUtils.hasText(yeePay)) {
            channelPayOrderContext.setRemark("当前支付方式不支持");
            channelPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(HaikePayScenePayOrderExtPt.class, BizScenario.valueOf(yeePay), ex -> ex.submitChannelPayOrder(channelPayOrderContext));
    }

    @Override
    public void queryChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        String yeePay = pay(payWayDO);
        if (!StringUtils.hasText(yeePay)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(HaikePayScenePayOrderExtPt.class, BizScenario.valueOf(yeePay), ex -> ex.queryChannelPayOrder(channelPayOrderQueryContext));
    }

    @Override
    public void closeChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        String yeePay = pay(payWayDO);
        if (!StringUtils.hasText(yeePay)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(HaikePayScenePayOrderExtPt.class, BizScenario.valueOf(yeePay), ex -> ex.closeChannelPayOrder(channelPayOrderQueryContext));
    }

    @Override
    public void queryChannelClosePayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        String yeePay = pay(payWayDO);
        if (!StringUtils.hasText(yeePay)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(HaikePayScenePayOrderExtPt.class, BizScenario.valueOf(yeePay), ex -> ex.queryChannelClosePayOrder(channelPayOrderQueryContext));

    }

    @Override
    public boolean refundApply(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        return true;
    }

    @Override
    public void refundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        PayWayDO payWayDO = channelRefundPayOrderContext.getPayWayDO();
        String yeePay = pay(payWayDO);
        if (!StringUtils.hasText(yeePay)) {
            channelRefundPayOrderContext.setRemark("当前支付方式不支持");
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(HaikePayScenePayOrderExtPt.class, BizScenario.valueOf(yeePay), ex -> ex.refundChannelPayOrder(channelRefundPayOrderContext));
    }

    @Override
    public void queryRefundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        PayWayDO payWayDO = channelRefundPayOrderContext.getPayWayDO();
        String yeePay = pay(payWayDO);
        if (!StringUtils.hasText(yeePay)) {
            channelRefundPayOrderContext.setRemark("当前支付方式不支持");
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(HaikePayScenePayOrderExtPt.class, BizScenario.valueOf(yeePay), ex -> ex.queryRefundChannelPayOrder(channelRefundPayOrderContext));
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> orderStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {

        CommonResult<PayOrderChannelResultDto> payOrderDOCommonResult = CommonResult.success();
        try {
            String paramsConfig = providerChannelConfigDO.getParamsConfig();
            // 海科配置信息
            HaikePayConfig haikePayConfig = JsonUtils.parseObject(paramsConfig, HaikePayConfig.class);
            // 获取响应内容
            String result = haikePayCommonParamsHandle.decrypt(request,haikePayConfig,true);
            log.info("海科支付通知明文 plainText:{}",result);
            if (StrUtil.isEmpty(result)) {
                log.info("海科回调验签不通过,signature:{}", result);
                payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
                return payOrderDOCommonResult;
            }
            payOrderDOCommonResult.setData(commonParse(result));

        }
        catch (Exception e) {
            log.error("海科支付通知解析失败,失败信息 message: {}", e);
            payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
        }

        log.info("海科收到回调,解析后的订单数据为 payOrderDO:{}", JsonUtils.toJsonStringNotNull(payOrderDOCommonResult.getData()));
        return payOrderDOCommonResult;
    }



    public PayOrderChannelResultDto commonParse(String params) {
        PayOrderDO updartePayOrderDO = new PayOrderDO();
        HaikePrePayNotifyResp respData = JsonUtils.parseObject(params, HaikePrePayNotifyResp.class);
        switch (respData.getTrade_status()) {
            case "3":
                updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.PAYING.getCode());
                updartePayOrderDO.setBankMsg("");
                break;
            case "1":
                updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.SUCCESS.getCode());
                updartePayOrderDO.setBankMsg("");
                break;
            case "2":
                updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.FAIL.getCode());
                break;
        }
        if (StringUtils.hasText(respData.getEnd_time())) {
            try {
                updartePayOrderDO.setSuccessTime(DateUtils.shanghaiDate(respData.getEnd_time(), DatePattern.NORM_DATETIME_PATTERN));
            }
            catch (Exception e) {
                log.error("海科订单回调返回 时间格式化错误 dateTime:{},message:{}", respData.getEnd_time(), e.getMessage());
            }
        }
        updartePayOrderDO.setPayOrderNo(respData.getOut_trade_no());
        updartePayOrderDO.setChannelOrderNo(respData.getBank_trade_no());
        updartePayOrderDO.setInsOrderNo(respData.getTrade_no());
        if (respData.getOrder_amount() != null) {
            updartePayOrderDO.setPayAmount(new BigDecimal(respData.getOrder_amount()));
        }

        if (StrUtil.isNotEmpty(respData.getOpenid())) {
            updartePayOrderDO.setUserId(respData.getOpenid());
        }

        PayOrderChannelResultDto payOrderChannelResultDto = new PayOrderChannelResultDto();
        payOrderChannelResultDto.setUpdatePayOrderDO(updartePayOrderDO);
        payOrderChannelResultDto.setCallBackBody(params);
        return payOrderChannelResultDto;
    }



    @Override
    public CommonResult<PayOrderChannelResultDto> orderStatusParse(HttpServletRequest request) {
        return null;
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> merAuthParse(HttpServletRequest request) {
        return null;
    }

    @Override
    public PayOrderDO closeOrderStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {
        return null;
    }

    @Override
    public CommonResult<CsOrderNotifyDto> csOrderStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {

        CommonResult<CsOrderNotifyDto> payOrderDOCommonResult = CommonResult.success();
        try {
            String paramsConfig = providerChannelConfigDO.getParamsConfig();
            // 海科配置信息
            YeePayConfig yeePayConfig = JsonUtils.parseObject(paramsConfig, YeePayConfig.class);
            // 获取响应内容
            String result = request.getParameter("response");

            log.info("海科支付清算通知密文 {}", result);
            String plainText = YeePayRsaUtil.decrypt(result, yeePayConfig,true);
            log.info("海科支付清算通知明文 {}", plainText);

            if (StrUtil.isEmpty(plainText)) {
                log.info("海科支付清算回调验签不通过,signature:{}", plainText);
                payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
            }
            CsOrderNotifyDto csOrderNotifyDto = new CsOrderNotifyDto();
            csOrderNotifyDto.setCallBackBody(plainText);
            PayOrderDO updatePayOrderDO = new PayOrderDO();
            YeePayCsNotify respData = JsonUtils.parseObject(plainText, YeePayCsNotify.class);
            switch (respData.getStatus()) {
                case "SUCCESS":
                    updatePayOrderDO.setCsState(1);
                    updatePayOrderDO.setCsTime(DateUtils.shanghaiDate(respData.getCsSuccessDate(),DatePattern.NORM_DATETIME_PATTERN));
                    break;
            }
            updatePayOrderDO.setPayOrderNo(respData.getOrderId());
            csOrderNotifyDto.setPayOrderDO(updatePayOrderDO);
            payOrderDOCommonResult.setData(csOrderNotifyDto);
        }
        catch (Exception e) {
            log.error("海科支付清算回调解析失败,失败信息 message: {}", e);
            payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
        }

        log.info("海科收到清算回调,解析后的订单数据为 payOrderDO:{}", JsonUtils.toJsonStringNotNull(payOrderDOCommonResult.getData()));
        return payOrderDOCommonResult;
    }

    @Override
    public CommonResult<RefundOrderNotifyDto> refundStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {

        CommonResult<RefundOrderNotifyDto> payOrderDOCommonResult = CommonResult.success();
        try {
            String paramsConfig = providerChannelConfigDO.getParamsConfig();
            // 海科配置信息
            HaikePayConfig haikePayConfig = JsonUtils.parseObject(paramsConfig, HaikePayConfig.class);
            // 获取响应内容
            String result = haikePayCommonParamsHandle.decrypt(request,haikePayConfig,true);
            log.info("海科支付退款通知明文 {}", result);
            if(StrUtil.isEmpty(result)){
                log.info("海科支付退款回调验签不通过,signature:{}", result);
                payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
                return payOrderDOCommonResult;
            }

            RefundOrderNotifyDto refundOrderNotifyDto = new RefundOrderNotifyDto();
            refundOrderNotifyDto.setCallBackBody(result);
            RefundOrderDO updateRefundOrderDO = new RefundOrderDO();
            HaikeOrderRefundNotifyResp respData = JsonUtils.parseObject(result, HaikeOrderRefundNotifyResp.class);
            switch (respData.getTrade_status()) {
                case "1":
                    updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.REFUND.getCode());
                    updateRefundOrderDO.setBankMsg("");
                    break;
                case "2":
                    updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.FAIL.getCode());
                    break;
                default:
                    updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.REFUNDING.getCode());
            }

            updateRefundOrderDO.setRefundOrderNo(respData.getRefund_no());
            if (respData.getRefund_amount() != null ) {
                updateRefundOrderDO.setActualRefundAmount(new BigDecimal(respData.getRefund_amount()));
            }
            refundOrderNotifyDto.setRefundOrder(updateRefundOrderDO);
            payOrderDOCommonResult.setData(refundOrderNotifyDto);
        }
        catch (Exception e) {
            log.error("海科退款订单查询 请求失败,失败信息 message: {}", e);
            payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
        }

        log.info("海科收到退款回调,解析后的订单数据为 payOrderDO:{}", JsonUtils.toJsonStringNotNull(payOrderDOCommonResult.getData()));
        return payOrderDOCommonResult;
    }

    @Override
    public String channelPayCode(PayWayDO payWayDO) {
        return pay(payWayDO);
    }

    @Override
    public Long queryChannelPayAppid(ChannelPayOrderContext channelPayOrderContext) {
        PayWayDO payWayDO = channelPayOrderContext.getPayWayDO();
        PayOrderDO payOrderDO = channelPayOrderContext.getPayOrderDO();
        PayOrderCommonParamReq.Extra extra = channelPayOrderContext.getExtra();
        if (extra == null && StringUtils.hasText(payOrderDO.getExtra())) {
            extra = JsonUtils.parseObject(payOrderDO.getExtra(), PayOrderCommonParamReq.Extra.class);
        }
        PayEnums.PAY_TYPE payType = PayEnums.PAY_TYPE.fromByCode(payWayDO.getType());
        PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.fromByType(payWayDO.getSubType());
        ProviderChannelConfigDO providerChannelConfigDO = channelPayOrderContext.getProviderChannelConfigDO();
        if (extra != null && payType.getCode().equals(PayEnums.PAY_TYPE.WECHAT.getCode())) {
            switch (scenePayType) {
                case QR:
                case JSAPI:
                    return providerChannelConfigDO.getWechatDefaultPubId();
                case APP:
                case H5:
                case MINI_APP:
                    return providerChannelConfigDO.getWechatDefaultMiniId();
                default:
            }
        }

        return null;
    }
    @Override
    public String callBackResponse(CommonResult commonResult) {
        Map<String,String> result = new HashMap<>();
        if (commonResult.isSuccess()) {
            result.put("return_code","SUCCESS");
        }
        else {
            result.put("return_code","FAIL");
        }
        return JSONUtil.toJsonStr(result);
    }

    @Override
    public boolean wechatLink(ChannelPayOrderContext channelPayOrderContext) {
        // 额外参数信息
        PayOrderCommonParamReq.Extra extra = channelPayOrderContext.getExtra();
        if(extra.getOriginalType() == CommonEnum.YES_NO_TYPE.NO.getCode()){
            PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.from(channelPayOrderContext.getPayWayDO().getSubType());
            if (scenePayType == null) {
                return false;
            }
            PayEnums.PAY_TYPE payType = PayEnums.PAY_TYPE.fromByCode(channelPayOrderContext.getPayWayDO().getType());
            if(payType == null){
                return false;
            }
            switch (scenePayType) {
                case H5:
                case APP:
                    if(payType == PayEnums.PAY_TYPE.WECHAT){
                        return true;
                    }
            }
        }

        return false;
    }


    /**
     * 海科支付方式路由
     * 
     * @param payWayDO
     * @return
     */
    private String pay(PayWayDO payWayDO) {
        PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.from(payWayDO.getSubType());
        if (scenePayType == null) {
            return null;
        }
        PayEnums.PAY_TYPE payType = PayEnums.PAY_TYPE.fromByCode(payWayDO.getType());
        if(payType == null){
            return null;
        }
        switch (payType) {
            case WECHAT:
                switch (scenePayType) {
                    case QR:
                        return LeshuaPayExtConstant.NATIVE_PAY;
                    case SCAN:
                        return LeshuaPayExtConstant.SCAN_PAY;
                    default:
                        return LeshuaPayExtConstant.JSAPI_PAY;
                }
            case ALIPAY:
                switch (scenePayType) {
                    case QR:
                    case H5:
                    case APP:
                        return LeshuaPayExtConstant.NATIVE_PAY;
                    case JSAPI:
                        return LeshuaPayExtConstant.JSAPI_PAY;
                    case MINI_APP:
                        return LeshuaPayExtConstant.MINI_APP;
                    case SCAN:
                        return LeshuaPayExtConstant.SCAN_PAY;
                }
            case UNION_APY:
                switch (scenePayType) {
                    case JSAPI:
                        return LeshuaPayExtConstant.JSAPI_PAY;
                    case QR:
                        return LeshuaPayExtConstant.NATIVE_PAY;
                    case SCAN:
                        return LeshuaPayExtConstant.SCAN_PAY;
                }
        }
        return null;
    }
}
